package com.lambkit.web.render;

import com.jfinal.kit.JsonKit;
import com.jfinal.kit.Okv;
import com.jfinal.render.Render;
import com.jfinal.render.RenderException;
import com.jfinal.render.RenderManager;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author yangyong(孤竹行)
 */
public class LambkitErrorRender extends Render {

    protected static final String contentTypeHtml = "text/html; charset=" + getEncoding();
    protected static final String contentTypeJson = "application/json; charset=" + getEncoding();

    protected static final String version = "<center><a href='https://gitee.com/lambkit/lambkit' target='_blank'><b>Powered by JFLambkit</b></a></center>";

    protected static final byte[] html404 = ("<html><head><title>404 Not Found</title></head><body bgcolor='white'><center><h1>404 Not Found</h1></center><hr>" + version + "</body></html>").getBytes();
    protected static final byte[] html500 = ("<html><head><title>500 Internal Server Error</title></head><body bgcolor='white'><center><h1>500 Internal Server Error</h1></center><hr>" + version + "</body></html>").getBytes();
    protected static final byte[] html400 = ("<html><head><title>400 Bad Request</title></head><body bgcolor='white'><center><h1>400 Bad Request</h1></center><hr>" + version + "</body></html>").getBytes();
    protected static final byte[] html401 = ("<html><head><title>401 Unauthorized</title></head><body bgcolor='white'><center><h1>401 Unauthorized</h1></center><hr>" + version + "</body></html>").getBytes();
    protected static final byte[] html403 = ("<html><head><title>403 Forbidden</title></head><body bgcolor='white'><center><h1>403 Forbidden</h1></center><hr>" + version + "</body></html>").getBytes();

    protected static final byte[] json404 = JsonKit.toJson(Okv.of("state", "fail").set("code", 0).set("msg", "404 Not Found")).getBytes();
    protected static final byte[] json500 = JsonKit.toJson(Okv.of("state", "fail").set("code", 0).set("msg", "500 Internal Server Error")).getBytes();
    protected static final byte[] json400 = JsonKit.toJson(Okv.of("state", "fail").set("code", 0).set("msg", "400 Bad Request")).getBytes();
    protected static final byte[] json401 = JsonKit.toJson(Okv.of("state", "fail").set("code", 0).set("msg", "401 Unauthorized")).getBytes();
    protected static final byte[] json403 = JsonKit.toJson(Okv.of("state", "fail").set("code", 0).set("msg", "403 Forbidden")).getBytes();

    protected static final Map<Integer, byte[]> errorHtmlMap = new HashMap<>();
    protected static final Map<Integer, byte[]> errorJsonMap = new HashMap<>();

    protected static final Map<Integer, String> errorViewMap = new HashMap<Integer, String>();

    static {
        errorHtmlMap.put(404, html404);
        errorHtmlMap.put(500, html500);
        errorHtmlMap.put(400, html400);
        errorHtmlMap.put(401, html401);
        errorHtmlMap.put(403, html403);

        errorJsonMap.put(404, json404);
        errorJsonMap.put(500, json500);
        errorJsonMap.put(400, json400);
        errorJsonMap.put(401, json401);
        errorJsonMap.put(403, json403);
    }

    protected int errorCode;
    protected String viewOrJson;

    public LambkitErrorRender(int errorCode, String viewOrJson) {
        this.errorCode = errorCode;
        this.viewOrJson = viewOrJson;
    }

    public LambkitErrorRender(int errorCode) {
        this.errorCode = errorCode;
    }

    /**
     * 设置异常发生时响应的错误页面
     */
    public static void setErrorView(int errorCode, String errorView) {
        errorViewMap.put(errorCode, errorView);
    }

    public static String getErrorView(int errorCode) {
        return errorViewMap.get(errorCode);
    }

    /**
     * 设置异常发生时响应的 html 内容
     */
    public static void setErrorHtmlContent(int errorCode, String htmlContent) {
        try {
            errorHtmlMap.put(errorCode, htmlContent.getBytes(getEncoding()));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置异常发生时响应的 json 内容
     */
    public static void setErrorJsonContent(int errorCode, String jsonContent) {
        try {
            errorJsonMap.put(errorCode, jsonContent.getBytes(getEncoding()));
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    public void render() {
        response.setStatus(getErrorCode());	// HttpServletResponse.SC_XXX_XXX

        String ct = request.getContentType();
        boolean isJsonContentType = ct != null && ct.indexOf("json") != -1;

        // 支持 me.setErrorView(xxx.html) 配置
        // 注意：针对 json 的 setErrorJsonContent(...) 直接覆盖掉了默认值，会走后面的 response.getOutputStream().write(...)
        if (viewOrJson == null) {
            if (! isJsonContentType) {
                viewOrJson = getErrorView(getErrorCode());
            }
        }

        // render with viewOrJson
        if (viewOrJson != null) {
            if (isJsonContentType) {
                RenderManager.me().getRenderFactory().getJsonRender(viewOrJson).setContext(request, response).render();
            } else {
                RenderManager.me().getRenderFactory().getRender(viewOrJson).setContext(request, response).render();
            }
            return;
        }

        // render with html content
        OutputStream os = null;
        try {
            response.setContentType(isJsonContentType ? contentTypeJson : contentTypeHtml);
            os = response.getOutputStream();
            os.write(isJsonContentType ? getErrorJson() : getErrorHtml());
        } catch (Exception e) {
            if (e instanceof IOException) {
                close(os);
            }
            throw new RenderException(e);
        }
    }

    public byte[] getErrorHtml() {
        byte[] ret = errorHtmlMap.get(getErrorCode());
        return ret != null ? ret : ("<html><head><title>" + errorCode + " Error</title></head><body bgcolor='white'><center><h1>" + errorCode + " Error</h1></center><hr>" + version + "</body></html>").getBytes();
    }

    public byte[] getErrorJson() {
        byte[] ret = errorJsonMap.get(getErrorCode());
        return ret != null ? ret : JsonKit.toJson(Okv.of("state", "fail").set("msg", errorCode + " Error")).getBytes();
    }

    public int getErrorCode() {
        return errorCode;
    }
}